import { RigidBody } from "../RigidBody";
import { BitmapRenderer } from "./BitmapRenderer";
import { Camera } from "./Camera";
import { BoxCollider, CircleCollider, EdgeCollider } from "./Collider";
import { EditorSystem } from "./EditorSystem";
import { GameObject } from "./GameObject";
import { LifeCycleSystem } from "./LifeCycleSystem";
import { MouseControlSystem } from "./MouseControlSystem";
import { RectangleShapeRenderer } from "./RectangleShapeRenderer";
import { RenderingSystem } from "./RenderingSystem";
import { System } from "./System";
import { TextRenderer } from "./TextRenderer";
import { Transform } from "./Transform";
import { TransformSystem } from "./TransformSystem";


export class GameEngine {
    objects: { [id: string]: GameObject; } = {};

    private rootGameObject: GameObject;

    private systems: System[] = [];

    runMode: "edit" | "running" = location.href.indexOf("mode=edit") > 0 ? 'edit' : 'running'

    // runMode: "edit" | "running" ='edit'

    constructor() {
        this.registerBehaviour(Transform);
        this.registerBehaviour(TextRenderer);
        this.registerBehaviour(BitmapRenderer);
        this.registerBehaviour(Camera);
        this.registerBehaviour(RectangleShapeRenderer);
        this.registerBehaviour(RigidBody);
        this.registerBehaviour(CircleCollider);
        this.registerBehaviour(EdgeCollider);
        this.registerBehaviour(BoxCollider);
        this.registerSystem(new TransformSystem());
        this.registerSystem(new MouseControlSystem());
        this.registerSystem(new RenderingSystem());
        if (this.runMode === 'running') {
            this.registerSystem(new LifeCycleSystem());
        }
        else if (this.runMode === 'edit') {
            this.registerSystem(new EditorSystem());
        }
    }

    onUpdate?: (duringTime: number) => void;

    private behaviourMap: any = {};

    registerSystem(system: System) {
        this.systems.push(system);
        system.engine = this;
    }

    startSystem() {
        for (const system of this.systems) {
            system.rootGameObject = this.rootGameObject;
            system.onStart();
        }
    }

    getSystems() {
        return this.systems;
    }

    registerBehaviour(behaviourClass) {
        this.behaviourMap[behaviourClass.name] = behaviourClass;
    }

    private getBehaviourByName(name: string) {
        const result = this.behaviourMap[name];
        if (!result) {
            throw new Error(`${name}不是一个有效的Behaviour`);
        }
        return result;
    }


    createPrefab(prefabName: string) {
        const content = getResource(prefabName);
        const prefabConfig = JSON.parse(content);
        const prefabRootGameObject = this.createGameObject(prefabConfig);
        return prefabRootGameObject;
    }


    start(images: string[], scenes: string[], currentScene: string, onComplete?: Function) {
        const initialTime = Date.now();
        loadMultiImage(images, () => {
            loadMultiText(scenes, () => {

                const content = getResource(currentScene);
                const sceneConfig = JSON.parse(content);
                this.loadScene(sceneConfig);
                this.startSystem();

                const advancedTime = Date.now() - initialTime;
                this.enterFrame(advancedTime);
                if (onComplete) {
                    onComplete();
                }
            })

        });
    }

    private lastTime = 0;

    private iterationCurrentTime = 0;

    /**
     * 心跳控制器
     */
    enterFrame(advancedTime: number) {
        const duringTime = advancedTime - this.lastTime;
        this.lastTime = advancedTime;
        this.onEnterFrame(duringTime);
        requestAnimationFrame((advancedTime) => this.enterFrame(advancedTime));
    }

    private onEnterFrame(duringTime: number) {

        this.iterationCurrentTime += duringTime;
        const fps = 60;
        const mileSecondPerTick = 1000 / fps;
        while (this.iterationCurrentTime >= mileSecondPerTick) {
            this.iterationCurrentTime -= mileSecondPerTick;
            for (const system of this.systems) {
                system.onTick(mileSecondPerTick);
            }
        }

        for (const system of this.systems) {
            system.onFrame(duringTime);
        }
    }

    getGameObject(id: string) {
        return this.objects[id];
    }


    loadScene(sceneConfig: any) {
        const object = this.createGameObject(sceneConfig);
        this.rootGameObject = object;
        this.rootGameObject.active = true;
    }

    private createGameObject(config: any): GameObject {
        const { children, id } = config;
        let gameObject: GameObject = new GameObject();
        gameObject.engine = this;
        if (children) {
            for (const child of children) {
                const childGameObject = this.createGameObject(child);
                gameObject.addChild(childGameObject);
            }
        }
        const behaviours = config.behaviours || [];
        for (const behaviourConfig of behaviours) {
            const behaviourClass = this.getBehaviourByName(behaviourConfig.name);
            const behaviour = new behaviourClass();
            const behaviourProperties = behaviourConfig.properties || {};
            for (let key in behaviourProperties) {
                const value = behaviourProperties[key];
                behaviour[key] = value;
            }
            gameObject.addBehaviour(behaviour);
        }

        if (id) {
            this.objects[id] = gameObject;
            gameObject.id = id;
        }
        return gameObject;
    }


}

const store: any = {};

export function getResource(url: string) {
    const resource = store[url];
    if (!resource) {
        throw new Error(`${url}没有加载`)
    };
    return resource;
}


export function loadText(url: string, onSuccess: Function) {
    const xhr = new XMLHttpRequest();
    xhr.onload = () => {
        onSuccess(xhr.responseText);
    }
    xhr.open('get', url);
    xhr.send();
}
export function loadMultiImage(list: string[], onSuccess: Function) {
    let count = 0;
    for (const url of list) {
        loadImage(url, function onLoadItem(img) {
            count++;
            store[url] = img;
            if (count === list.length) {
                onSuccess();
            }
        })
    }
}

function loadMultiText(list: string[], onSuccess: Function) {
    let count = 0;
    for (const url of list) {
        loadText(url, function onLoadItem(img) {
            count++;
            store[url] = img;
            if (count === list.length) {
                onSuccess();
            }
        })
    }
}

function loadImage(url: string, onSuccess: Function) {
    const img = new Image();
    img.onload = function () {
        onSuccess(img);
    }
    //异步过程
    img.src = url;
}
